﻿using System;
using System.Diagnostics;
#if NET8_0_OR_GREATER
using System.Diagnostics.CodeAnalysis;
#endif
using System.Text.RegularExpressions;
using DimensionsHelper.Common.Contracts.Table;
using DimensionsHelper.Common.Table;
// ReSharper disable RedundantSuppressNullableWarningExpression

namespace DimensionsHelper.Services.CsvReader;

[DebuggerDisplay("Value = {Value}")]
public class CsvCell : ICell
{
    private string? _text;
    private object _value;

    private EventHandler<CellValueChangedArgs>? _valueChangedHandler;
    private ValueType _valueType;

    internal CsvCell(ILine line, int index)
    {
        Line = line;
        _value = string.Empty;
        _valueType = ValueType.String;
        Index = index;
        _text = null;
    }

    public bool IsString => _valueType == ValueType.String;

    public bool IsInteger => _valueType == ValueType.Integer;

    public bool IsDouble => _valueType == ValueType.Double;

    public bool IsChar => _value is string { Length: 1 };

    public event EventHandler<CellValueChangedArgs> ValueChanged
    {
        add => _valueChangedHandler += value;
        remove => _valueChangedHandler -= value;
    }

    public int Index { get; }

    public ILine Line { get; }

    public bool IsEmpty => string.IsNullOrEmpty(Text.Trim());

    public int Length => Text.Length;

    public string Text
    {
        get
        {
            if (_text != null)
            {
                return _text;
            }

            if (_value is string str)
            {
                var trimString = str.Trim();
                _text = string.IsNullOrEmpty(trimString) ? string.Empty : str;
            }
            else
            {
                _text = _value.ToString() ?? "";
            }

            return _text;
        }
    }

    public object Value
    {
        get => _value;
        set
        {
            var oldText = Text;

            _value = value;

            switch (_value)
            {
                case string str:
                {
                    if (int.TryParse(str, out var intValue))
                    {
                        _value = intValue;
                        _valueType = ValueType.Integer;
                    }
                    else if (double.TryParse(str, out var dblValue))
                    {
                        _value = dblValue;
                        _valueType = ValueType.Double;
                    }
                    else
                    {
                        _valueType = ValueType.String;
                    }

                    var trimString = str.Trim();
                    _text = string.IsNullOrEmpty(trimString) ? string.Empty : str;
                    break;
                }
                case int intValue:
                {
                    _valueType = ValueType.Integer;
                    _text = intValue.ToString();
                    break;
                }
                case float:
                case double:
                {
                    _valueType = ValueType.Double;
                    _text = _value.ToString();
                    break;
                }
                default:
                    throw new Exception($"不支持的数据类型：{_value.GetType().Name}");
            }

            _valueChangedHandler?.Invoke(this, new CellValueChangedArgs(oldText, _text!));
        }
    }

    public char CharAt(int charIndex)
    {
        var cellText = Text;

        if (charIndex >= 0 && charIndex < cellText.Length)
        {
            return cellText[charIndex];
        }

        throw new IndexOutOfRangeException();
    }

    public bool IsMatch(Regex regexp)
    {
        return regexp.IsMatch(Text);
    }

    public bool IsMatch(string pattern)
    {
        return new Regex(pattern).IsMatch(Text);
    }

    public bool IsMatch(Func<string, bool> predicate)
    {
        return predicate(Text);
    }

    public bool TryGetValue<T>(
#if NET8_0_OR_GREATER
            [NotNullWhen(true)]
            out T? finalValue)
#else
        out T finalValue)
#endif
    {
        finalValue = default!;
        var type = typeof(T);
        var cellText = Text;

        if (type == _value.GetType())
        {
            finalValue = (T)_value;
            return true;
        }

        if (type == typeof(string))
        {
            finalValue = (T)(object)cellText!;
            return true;
        }

        try
        {
            finalValue = (T)Convert.ChangeType(cellText, type);
            return true;
        }
        catch
        {
            return false;
        }
    }

    private enum ValueType
    {
        String,
        Integer,
        Double
    }
}